#include "liverecorderview.h"
#include "addattentionsdialog.h"
#include "captchaassisttask.h"
#include "libs/base/service/downloader.h"
#include "libs/controls/loadingframe.h"
#include "libs/controls/toastlabel.h"
#include "libs/core/model/attentionmodel.h"
#include "libs/core/model/livemodel.h"
#include "libs/core/model/model.h"
#include "libs/core/model/presentermodel.h"
#include "liverecorder.h"
#include "liverecordertype.h"
#include "livestatusitemdelegate.h"
#include "moreitemwidget.h"
#include "recordtaskmanager.h"
#include "ui_liverecorderview.h"
#include "usersettings.h"
#include "widgets/checkboxitemdelegate.h"
#include "widgets/nofocusitemdelegate.h"
#include "yingce.h"
#include <QCursor>
#include <QFileDialog>
#include <QFileInfo>
#include <QHeaderView>
#include <QMenu>
#include <QResizeEvent>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QTableView>
#include <QTimer>
#include <QUrl>
#include <QVBoxLayout>

LiveRecorderView::LiveRecorderView(LiveRecorder* recorder, QWidget* parent)
    : yc::ExtensionView { recorder, parent }, ui(new Ui::LiveRecorderView)
{
    model_ = new QStandardItemModel(this);
    model_->setHorizontalHeaderLabels(
            QStringList { QString(), tr("Presenter"), tr("Status"), QString() });
    model_->setHeaderData(NAME_COLUMN, Qt::Horizontal, int(Qt::AlignLeft | Qt::AlignVCenter),
                          Qt::TextAlignmentRole);
    model_->setHeaderData(STATUS_COLUMN, Qt::Horizontal, int(Qt::AlignLeft | Qt::AlignVCenter),
                          Qt::TextAlignmentRole);
    model_->setHeaderData(MORE_COLUMN, Qt::Horizontal, int(Qt::AlignRight | Qt::AlignVCenter),
                          Qt::TextAlignmentRole);

    filter_model_ = new QSortFilterProxyModel(this);
    filter_model_->setSourceModel(model_);
    filter_model_->setFilterCaseSensitivity(Qt::CaseInsensitive);
    filter_model_->setFilterKeyColumn(NAME_COLUMN);
    filter_model_->setFilterRole(Qt::DisplayRole);

    ui->setupUi(this);
    ui->all_checkbox->setEnabled(false);
    ui->delete_button->setEnabled(false);
    ui->start_button->setEnabled(false);
    ui->stop_button->setEnabled(false);
    ui->captcha_button->hide();

    ui->table_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->table_view->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->table_view->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->table_view->setItemDelegate(new NoFocusItemDelegate(this));
    ui->table_view->setItemDelegateForColumn(CHECK_STATE_COLUMN, new CheckBoxItemDelegate(this));
    ui->table_view->setItemDelegateForColumn(STATUS_COLUMN, new LiveStatusItemDelegate(this));
    ui->table_view->setContextMenuPolicy(Qt::NoContextMenu);
    ui->table_view->setShowGrid(false);
    ui->table_view->setIconSize(QSize(48, 48));
    ui->table_view->setModel(filter_model_);

    auto header = ui->table_view->verticalHeader();
    header->hide();
    header->setDefaultSectionSize(56);

    header = ui->table_view->horizontalHeader();
    header->setSectionResizeMode(QHeaderView::ResizeMode::Fixed);
    header->setSectionResizeMode(STATUS_COLUMN, QHeaderView::Stretch);
    header->setSectionsClickable(false);
    header->setSectionsMovable(false);
    header->setDefaultSectionSize(120);
    header->resizeSection(CHECK_STATE_COLUMN, 40);
    header->resizeSection(NAME_COLUMN, 240);
    header->resizeSection(STATUS_COLUMN, 420);
    header->resizeSection(MORE_COLUMN, 120);

    QTimer* loading_timer = new QTimer(this);
    loading_timer->setInterval(50);
    connect(loading_timer, &QTimer::timeout, this, [this] {
        const int count = model_->rowCount();
        for (int i = 0; i < count; ++i) {
            if (auto p = model_->item(i, STATUS_COLUMN)) {
                if (p->data(RECORD_STATUS_ROLE).toInt() == MERGEING_STATUS) {
                    int idx = p->data(LOADING_INDEX_ROLE).toInt();
                    idx = ++idx % 16;
                    p->setData(idx, LOADING_INDEX_ROLE);
                }
            }
        }
    });
    loading_timer->start();

    connect(ui->search_edit, &QLineEdit::editingFinished, this, [this] {
        filter_model_->setFilterWildcard(QString("*%1*").arg(ui->search_edit->text().trimmed()));
    });

    connect(recorder->recordTaskManager(), &RecordTaskManager::aboutToStart, this,
            [this](int attention) {
                if (auto p = getItem(attention)) {
                    if (auto q = model_->item(p->row(), STATUS_COLUMN)) {
                        q->setData(RECORDING_STATUS, RECORD_STATUS_ROLE);
                        q->setData(0, RECORD_DURATION_ROLE);
                        q->setData(QString(), MESSAGE_ROLE);
                    }
                    updateRecordUiStatus();
                    updateUiStatus();
                }
            });
    connect(recorder->recordTaskManager(), &RecordTaskManager::positionChanged, this,
            [this](int attention, qint64 pos) {
                Q_UNUSED(pos)
                if (auto p = getItem(attention)) {
                    if (auto q = model_->item(p->row(), STATUS_COLUMN)) {
                        q->setData(pos, RECORD_DURATION_ROLE);
                    }
                    updateRecordUiStatus();
                }
            });
    connect(recorder->recordTaskManager(), &RecordTaskManager::finished, this,
            [this](int attention, const QString& filename, qint64 duration) {
                Q_UNUSED(filename)
                Q_UNUSED(duration)
                if (auto p = getItem(attention)) {
                    if (auto q = model_->item(p->row(), STATUS_COLUMN)) {
                        q->setData(0, RECORD_STATUS_ROLE);
                        q->setData(duration, RECORD_DURATION_ROLE);
                        q->setData(QString(), MESSAGE_ROLE);
                    }
                    updateRecordUiStatus();
                    updateUiStatus();
                }
            });
    connect(recorder->recordTaskManager(), &RecordTaskManager::failed, this,
            [this](int attention, int error, const QString& message) {
                Q_UNUSED(error)
                if (auto p = getItem(attention)) {
                    if (auto q = model_->item(p->row(), STATUS_COLUMN)) {
                        q->setData(0, RECORD_STATUS_ROLE);
                        q->setData(0, RECORD_DURATION_ROLE);
                        q->setData(message, MESSAGE_ROLE);
                    }
                    updateRecordUiStatus();
                    updateUiStatus();
                }
            });
    connect(YingCe::instance()->downloader(), &yc::Downloader::downloaded, this,
            &LiveRecorderView::updateAvatar);

    connect(ui->delete_button, &QPushButton::clicked, this, [this](bool checked) {
        Q_UNUSED(checked)
        auto recorder = qobject_cast<LiveRecorder*>(extension());
        recorder->deleteAttentions(checkedAttentions());
    });
    connect(ui->add_button, &QPushButton::clicked, this, [this](bool checked) {
        Q_UNUSED(checked)
        auto recorder = qobject_cast<LiveRecorder*>(extension());
        AddAttentionsDialog dlg { recorder, this };
        dlg.exec();
    });
    auto am = YingCe::instance()->model()->attentionModel();
    connect(am, &yc::AttentionModel::modelReset, this, [this] {
        auto recorder = qobject_cast<LiveRecorder*>(extension());
        recorder->recordTaskManager()->termiateAll();
        model_->setRowCount(0);
    });
    connect(am, &yc::AttentionModel::added, this, [this](yc::Attention* attention) {
        int row = model_->rowCount();
        // CheckState
        auto item = new QStandardItem;
        item->setData(attention->id, ID_ROLE);
        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
        item->setTextAlignment(Qt::AlignCenter);
        model_->appendRow(item);
        // Name
        auto name_item = new QStandardItem;
        name_item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
        model_->setItem(row, NAME_COLUMN, name_item);
        // Status
        auto status_item = new QStandardItem;
        status_item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
        status_item->setTextAlignment(Qt::AlignCenter);
        status_item->setData(attention->auto_check ? 1 : 0, AUTO_CHECK_ROLE);
        model_->setItem(row, STATUS_COLUMN, status_item);
        // More
        item = new QStandardItem;
        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
        model_->setItem(row, MORE_COLUMN, item);
        ui->table_view->setIndexWidget(
                filter_model_->mapFromSource(model_->index(row, MORE_COLUMN)),
                new MoreItemWidget(this, attention, ui->table_view));

        if (auto presenter = YingCe::instance()->model()->presenterModel()->presenter(
                    attention->presenter)) {
            const auto entry =
                    YingCe::instance()->model()->liveModel()->webEntry(presenter->platform);
            name_item->setText(presenter->nickname + "\n(" + entry.name + ")");
            // Avatar
            if (!presenter->avatar.isEmpty()) {
                const auto filename = QUrl { presenter->avatar }.fileName();
                const auto path = UserSettings::instance().resDir().absoluteFilePath(filename);
                if (QFileInfo::exists(path)) {
                    name_item->setIcon(QIcon { path });
                }
            }
            // status
            status_item->setData(presenter->status, LIVE_STATUS_ROLE);
            // count
            status_item->setData(QString::number(presenter->audience_count), AUDIENCE_COUNT_ROLE);
        }

        // Ui status
        ui->all_checkbox->setEnabled(true);
        ui->captcha_button->setVisible(needCaptcha());
    });
    connect(am, &yc::AttentionModel::changed, this, [this](yc::Attention* attention) {
        auto recorder = qobject_cast<LiveRecorder*>(extension());
        const int count = model_->rowCount();
        for (int i = 0; i < count; ++i) {
            if (model_->item(i, 0)->data(ID_ROLE).toInt() == attention->id) {
                auto status_item = model_->item(i, STATUS_COLUMN);
                status_item->setData(attention->auto_check ? 1 : 0, AUTO_CHECK_ROLE);
                // TODO: 如果是直播中且是自动录制，则开始自动录制
                if (attention->auto_record) {
                    const auto p = YingCe::instance()->model()->presenterModel()->presenter(
                            attention->presenter);
                    if (p && p->status == yc::ACTIVE_LIVE_STATUS) {
                        recorder->startRecording(attention->id);
                    }
                }
                break;
            }
        }
    });
    connect(am, &yc::AttentionModel::removed, this, [this](yc::Attention* attention) {
        auto recorder = qobject_cast<LiveRecorder*>(extension());
        auto rtm = recorder->recordTaskManager();
        const int count = model_->rowCount();
        for (int i = 0; i < count; ++i) {
            if (model_->item(i, 0)->data(ID_ROLE).toInt() == attention->id) {
                model_->removeRow(i);
                rtm->termiate(attention->id);
                break;
            }
        }
        // Ui status
        ui->captcha_button->setVisible(needCaptcha());
        updateAllCheckState();
        updateUiStatus();
    });
    connect(YingCe::instance()->model()->presenterModel(), &yc::PresenterModel::changed, this,
            [this](yc::Presenter* presenter) {
                if (auto item = findItem(presenter->id)) {
                    auto status_item = model_->item(item->row(), STATUS_COLUMN);
                    // 更新直播状态
                    status_item->setData(presenter->status, LIVE_STATUS_ROLE);
                    // 更新在线人数
                    status_item->setData(QString::number(presenter->audience_count),
                                         AUDIENCE_COUNT_ROLE);

                    // TODO: 如果是直播中且是自动录制，则开始自动录制
                    if (presenter->status == yc::ACTIVE_LIVE_STATUS) {
                        const int id = item->data(ID_ROLE).toInt();
                        if (auto a = YingCe::instance()->model()->attentionModel()->attention(id)) {
                            if (a->auto_record) {
                                auto recorder = qobject_cast<LiveRecorder*>(extension());
                                recorder->startRecording(id);
                            }
                        }
                    }
                }
            });

    connect(ui->start_button, &QPushButton::clicked, this, [this](bool checked) {
        Q_UNUSED(checked)
        auto recorder = qobject_cast<LiveRecorder*>(extension());
        auto rtm = recorder->recordTaskManager();
        const int count = model_->rowCount();
        for (int i = 0; i < count; ++i) {
            auto item = model_->item(i, CHECK_STATE_COLUMN);
            if (item->data(CHECK_STATE_ROLE).toInt() == Qt::Checked) {
                const int id = item->data(ID_ROLE).toInt();
                if (!rtm->isRunning(id)) {
                    // Ui status
                    ui->start_button->setEnabled(false);
                    recorder->startRecording(id);
                }
            }
        }
    });
    connect(ui->stop_button, &QPushButton::clicked, this, [this](bool checked) {
        Q_UNUSED(checked)
        auto recorder = qobject_cast<LiveRecorder*>(extension());
        auto rtm = recorder->recordTaskManager();
        const int count = model_->rowCount();
        for (int i = 0; i < count; ++i) {
            auto item = model_->item(i, CHECK_STATE_COLUMN);
            if (item->data(CHECK_STATE_ROLE).toInt() == Qt::Checked) {
                const int id = item->data(ID_ROLE).toInt();
                if (rtm->isRunning(id)) {
                    // Ui status
                    ui->stop_button->setEnabled(false);

                    if (auto status_item = model_->item(i, STATUS_COLUMN)) {
                        // 正在合成中
                        status_item->setData(0, LOADING_INDEX_ROLE);
                        status_item->setData(MERGEING_STATUS, RECORD_STATUS_ROLE);
                    }
                    rtm->stop(item->data(ID_ROLE).toInt());
                }
            }
        }
    });

    connect(recorder->liveViewManager(), &LiveViewManager::captchaStateChanged, this,
            [this]() { ui->captcha_button->setVisible(needCaptcha()); });
    connect(ui->captcha_button, &QPushButton::clicked, this, [this] {
        auto recorder = qobject_cast<LiveRecorder*>(extension());
        if (recorder->liveViewManager()->passedCaptcha()) {
            yc::ToastLabel::showMessage(tr("Has passed captcha."), this, 5000);
        } else {
            CaptchaAssistTask* t = new CaptchaAssistTask(recorder, this);
            t->start();
        }
    });

    connect(model_, &QStandardItemModel::itemChanged, this, [this](QStandardItem* item) {
        if (item->column() == CHECK_STATE_COLUMN) {
            updateAllCheckState();
            updateUiStatus();
        }
    });
    connect(ui->all_checkbox, &QCheckBox::clicked, this, [this](bool checked) {
        const int count = model_->rowCount();
        for (int i = 0; i < count; ++i) {
            auto item = model_->item(i, CHECK_STATE_COLUMN);
            if (item->isEnabled()) {
                item->setData(checked ? Qt::Checked : Qt::Unchecked, CHECK_STATE_ROLE);
            }
        }
        updateUiStatus();
    });
}

QList<int> LiveRecorderView::checkedAttentions() const
{
    QList<int> ids;
    const int count = model_->rowCount();
    for (int i = 0; i < count; ++i) {
        auto item = model_->item(i, CHECK_STATE_COLUMN);
        if (item->data(CHECK_STATE_ROLE).toInt() == Qt::Checked) {
            ids.push_back(item->data(ID_ROLE).toInt());
        }
    }
    return ids;
}

int LiveRecorderView::currentAttention() const
{
    return ui->table_view->currentIndex().data(ID_ROLE).toInt();
}

void LiveRecorderView::setCurrentAttention(int id)
{
    const int count = model_->rowCount();
    for (int i = 0; i < count; ++i) {
        if (model_->item(i, 0)->data(ID_ROLE).toInt() == id) {
            ui->table_view->selectRow(i);
            break;
        }
    }
}

void LiveRecorderView::showLoading()
{
    if (!loading_frame_) {
        loading_frame_ =
                new yc::LoadingFrame(tr("Please wait while loading the anchor list."), this);
    }
    loading_frame_->resize(size());
    loading_frame_->show();
    loading_frame_->raise();
}

void LiveRecorderView::hideLoading()
{
    if (loading_frame_) {
        loading_frame_->hide();
        loading_frame_->deleteLater();
        loading_frame_ = nullptr;
    }
}

void LiveRecorderView::resizeEvent(QResizeEvent* e)
{
    yc::ExtensionView::resizeEvent(e);
    if (loading_frame_) {
        loading_frame_->resize(e->size());
        loading_frame_->raise();
    }
}

QStandardItem* LiveRecorderView::findItem(int presenter) const
{
    auto am = YingCe::instance()->model()->attentionModel();
    const int count = model_->rowCount();
    for (int i = 0; i < count; ++i) {
        auto item = model_->item(i, 0);
        if (auto p = am->attention(item->data(ID_ROLE).toInt())) {
            if (p->presenter == presenter) {
                return item;
            }
        }
    }
    return nullptr;
}

QStandardItem* LiveRecorderView::getItem(int id) const
{
    const int count = model_->rowCount();
    for (int i = 0; i < count; ++i) {
        auto item = model_->item(i, 0);
        if (item->data(ID_ROLE).toInt() == id) {
            return item;
        }
    }
    return nullptr;
}

void LiveRecorderView::updateItemStatus(int id, const QString& message)
{
    if (auto p = getItem(id)) {
        if (auto q = model_->item(p->row(), STATUS_COLUMN)) {
            q->setText(message);
        }
    }
}

bool LiveRecorderView::isRunning() const
{
    bool running = false;
    auto recorder = qobject_cast<LiveRecorder*>(extension());
    auto rtm = recorder->recordTaskManager();
    const int count = model_->rowCount();
    for (int i = 0; i < count; ++i) {
        auto item = model_->item(i, CHECK_STATE_COLUMN);
        if (item->data(CHECK_STATE_ROLE).toInt() == Qt::Checked) {
            if (rtm->isRunning(item->data(ID_ROLE).toInt())) {
                running = true;
                break;
            }
        }
    }
    return running;
}

void LiveRecorderView::updateUiStatus()
{
    const int count = model_->rowCount();
    ui->all_checkbox->setEnabled(count > 0);
    ui->delete_button->setEnabled(count > 0 && canDelete());
    ui->start_button->setEnabled(count > 0 && canStart());
    ui->stop_button->setEnabled(count > 0 && canStop());
}

void LiveRecorderView::updateRecordUiStatus()
{
    ui->start_button->setEnabled(canStart());
    ui->stop_button->setEnabled(canStop());
}

void LiveRecorderView::updateAllCheckState()
{
    int checked_count = 0;
    const int count = model_->rowCount();
    for (int i = 0; i < count; ++i) {
        auto item = model_->item(i, CHECK_STATE_COLUMN);
        if (item->data(CHECK_STATE_ROLE).toInt() == Qt::Checked) {
            checked_count++;
        }
    }
    if (checked_count > 0 && checked_count == count) {
        ui->all_checkbox->setCheckState(Qt::Checked);
    } else if (checked_count == 0) {
        ui->all_checkbox->setCheckState(Qt::Unchecked);
    } else {
        ui->all_checkbox->setCheckState(Qt::PartiallyChecked);
    }
}

void LiveRecorderView::updateAvatar(const QString& filename)
{
    const auto name = QFileInfo { filename }.fileName();
    auto am = YingCe::instance()->model()->attentionModel();
    auto pm = YingCe::instance()->model()->presenterModel();
    const int count = model_->rowCount();
    for (int i = 0; i < count; ++i) {
        if (auto p = am->attention(model_->item(i, 0)->data(ID_ROLE).toInt())) {
            if (auto q = pm->presenter(p->presenter)) {
                if (!q->avatar.isEmpty() && QUrl(q->avatar).fileName() == name) {
                    model_->item(i, NAME_COLUMN)->setIcon(QIcon(filename));
                    break;
                }
            }
        }
    }
}

bool LiveRecorderView::canDelete() const
{
    auto recorder = qobject_cast<LiveRecorder*>(extension());
    auto rtm = recorder->recordTaskManager();
    const int count = model_->rowCount();
    for (int i = 0; i < count; ++i) {
        auto item = model_->item(i, CHECK_STATE_COLUMN);
        if (item->data(CHECK_STATE_ROLE).toInt() == Qt::Checked
            && !rtm->isRunning(item->data(ID_ROLE).toInt())) {
            return true;
        }
    }
    return false;
}

bool LiveRecorderView::canStart() const
{
    auto recorder = qobject_cast<LiveRecorder*>(extension());
    auto rtm = recorder->recordTaskManager();
    const int count = model_->rowCount();
    for (int i = 0; i < count; ++i) {
        auto item = model_->item(i, CHECK_STATE_COLUMN);
        if (item->data(CHECK_STATE_ROLE).toInt() == Qt::Checked
            && !rtm->isRunning(item->data(ID_ROLE).toInt())) {
            return true;
        }
    }
    return false;
}

bool LiveRecorderView::canStop() const
{
    auto recorder = qobject_cast<LiveRecorder*>(extension());
    auto rtm = recorder->recordTaskManager();
    const int count = model_->rowCount();
    for (int i = 0; i < count; ++i) {
        auto item = model_->item(i, CHECK_STATE_COLUMN);
        if (item->data(CHECK_STATE_ROLE).toInt() == Qt::Checked
            && rtm->isRunning(item->data(ID_ROLE).toInt())) {
            return true;
        }
    }
    return false;
}

bool LiveRecorderView::needCaptcha() const
{
    if (LiveRecorder::containsKuaiShouAttentions()) {
        auto recorder = qobject_cast<LiveRecorder*>(extension());
        return !recorder->liveViewManager()->passedCaptcha();
    }
    return false;
}
